/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2000 by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*
 * This file is a module that handles the logging features of the
 * DCS. All error messages that are generated by the DCS are kept in
 * a static array and accessed through one of the access functions
 * defined in this file.
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <syslog.h>
#include <libintl.h>

#include "dcs.h"


#define	SYSLOG_FMT	"<%d> %s"


/*
 * This is an array of strings representing all of the error and
 * informational messages that are used by the DCS. This includes
 * messages that are logged using syslog(3C) and those that are
 * displayed to the user through a message callback.
 */
static const char *dcs_err_fmt[] = {

	/*
	 * Network Errors:
	 */
	/* DCS_INIT_ERR	    */ "network initialization failed",
	/* DCS_NO_PORT	    */ "failed to acquire reserved port",
	/* DCS_CONNECT_ERR  */ "connection attempt failed",
	/* DCS_RECEIVE_ERR  */ "unable to receive message",
	/* DCS_OP_REPLY_ERR */ "unable to send message for %s operation",
	/* DCS_NO_SERV	    */ "%s service not found, using reserved port 665",
	/* DCS_DISCONNECT   */ "client disconnected",

	/*
	 * Session Errors:
	 */
	/* DCS_SES_HAND_ERR */ "failed to start a new session handler",
	/* DCS_ABORT_ERR    */ "abort attempt of session, %d, unsuccessful",
	/* DCS_VER_INVAL    */ "unsupported message protocol version %d.%d",
	/* DCS_SES_ABORTED  */ "session aborted",

	/*
	 * DR Request Errors:
	 */
	/* DCS_UNKNOWN_OP   */ "unknown operation requested",
	/* DCS_OP_FAILED    */ "operation failed",
	/* DCS_SEQ_INVAL    */ "invalid session establishment sequence",
	/* DCS_NO_SES_ESTBL */ "%s operation issued before session established",
	/* DCS_MSG_INVAL    */ "received an invalid message",
	/* DCS_CONF_CB_ERR  */ "confirm callback failed, aborting operation",
	/* DCS_MSG_CB_ERR   */ "message callback failed, continuing",
	/* DCS_BAD_RETRY    */ "retry value invalid (%d)",
	/* DCS_BAD_TIMEOUT  */ "timeout value invalid (%d)",
	/* DCS_RETRY	    */ "retrying operation, attempt %d",

	/*
	 * General Errors:
	 */
	/* DCS_NO_PRIV	    */ "permission denied",
	/* DCS_INT_ERR	    */ "internal error: %s: %s",
	/* DCS_UNKNOWN_ERR  */ "unrecognized error reported",
	/* DCS_BAD_OPT	    */ "illegal option (-%c), exiting",
	/* DCS_BAD_OPT_ARG  */ "illegal argument to -%c flag (%s), %s",
	/* DCS_CFGA_UNKNOWN */ "configuration administration unknown error",
	/* DCS_CFGA_ERR	    */ "%s: %s",
	/* DCS_RSRC_ERR	    */ "resource info init error (%d)",
	/* DCS_MSG_COUNT    */ NULL
};


/*
 * dcs_log_msg:
 *
 * Based on an error code, construct an error string and output it to
 * a logfile using syslog(3C). Note that the string will not be localized.
 */
void
dcs_log_msg(int priority, int err_code, ...)
{
	va_list	vap;
	char	err_str[MAX_MSG_LEN];
	char	syslog_str[MAX_MSG_LEN];


	/* check if error code is out of bounds */
	if ((err_code < 0) || (err_code >= DCS_MSG_COUNT)) {
		syslog(LOG_NOTICE, dcs_err_fmt[DCS_UNKNOWN_ERR]);
		return;
	}

	va_start(vap, err_code);
	(void) vsnprintf(err_str, MAX_MSG_LEN, dcs_err_fmt[err_code], vap);
	va_end(vap);

	/* prepend session identifier */
	snprintf(syslog_str, MAX_MSG_LEN, SYSLOG_FMT, curr_ses_id(), err_str);

	syslog(priority, syslog_str);

	if (standalone) {
		fprintf(stderr, "%s\n", syslog_str);
	}
}


/*
 * dcs_strerror:
 *
 * Return the format string associated with a supplied DCS specific
 * error code. dgettext(3C) is used to retrieve the localized version
 * of the format string.
 */
const char *
dcs_strerror(int err_code)
{
	/* check if code is out of bounds */
	if ((err_code < 0) || (err_code >= DCS_MSG_COUNT)) {
		return (dgettext(TEXT_DOMAIN, dcs_err_fmt[DCS_UNKNOWN_ERR]));
	}

	return (dgettext(TEXT_DOMAIN, dcs_err_fmt[err_code]));
}


/*
 * dcs_cfga_str:
 *
 * Assemble a string that describes a particular libcfgadm error code.
 * This string will contain both the platform dependent and platform
 * independent message strings available from a libcfgadm function call.
 * The resulting string will be localized indirectly through the call
 * to config_strerror() and the localized error string returned from
 * the libcfgadm operation.
 */
char *
dcs_cfga_str(char **err_strp, int err_code)
{
	const char	*ep;
	char		*buf;
	char		*err_str;


	/*
	 * Extract the platform specific message passed as
	 * a parameter, or use NULL to signal that no error
	 * string was passed.
	 */
	if (err_strp && *err_strp) {
		err_str = *err_strp;
	} else {
		err_str = NULL;
	}

	buf = (char *)malloc(MAX_MSG_LEN);

	if (buf == NULL) {
		dcs_log_msg(LOG_ERR, DCS_INT_ERR, "malloc", strerror(errno));
		return (NULL);
	}

	/* get the platform independent message */
	ep = config_strerror(err_code);

	if (ep == NULL) {
		ep = dgettext(TEXT_DOMAIN, dcs_err_fmt[DCS_CFGA_UNKNOWN]);
	}

	/*
	 * Check if a platform specific message was provided, and
	 * generate the appropriate message.
	 */
	if ((err_str != NULL) && (*err_str != '\0')) {
		snprintf(buf, MAX_MSG_LEN, "%s: %s\n", ep, err_str);
	} else {
		snprintf(buf, MAX_MSG_LEN, "%s\n", ep);
	}

	return (buf);
}


/*
 * dcs_dbg:
 *
 * Output a debugging message to a logfile using syslog(3C). The bits
 * in the debug mask specify the category of the message. They have
 * the following meanings:
 *
 * 		0x1 - the string contains basic information
 *		0x2 - the string contains message information
 *		0x4 - the string contains session information
 *		0x8 - the string contains state information
 *
 * The debug mask is compared to the global value of dcs_debug which is
 * set through a command line option. This determines whether or not
 * to output the message to the logfile.
 */
void
dcs_dbg(int dbg_mask, char *fmt, ...)
{
	va_list		vap;
	char		err_str[MAX_MSG_LEN];
	char		syslog_str[MAX_MSG_LEN];


	if ((dcs_debug & dbg_mask) == 0) {
		return;
	}

	va_start(vap, fmt);
	(void) vsnprintf(err_str, MAX_MSG_LEN, fmt, vap);
	va_end(vap);

	/* prepend session identifier */
	snprintf(syslog_str, MAX_MSG_LEN, SYSLOG_FMT, curr_ses_id(), err_str);

	syslog(LOG_DEBUG, syslog_str);

	if (standalone) {
		fprintf(stderr, "%s\n", syslog_str);
	}
}


/*
 * print_msg_hdr:
 *
 * Print selected information from the header for a given message. The
 * information logged includes the information needed to track the flow
 * of messages: opcode, send/receive, request/reply, and success/failure.
 */
void
print_msg_hdr(dcs_msg_type_t type, rdr_msg_hdr_t *hdr)
{
	static char *type_str[] = {
		"INVALID TYPE",
		"RDR_REQUEST",
		"RDR_REPLY"
	};

	static char *op_str[] = {
		"RDR_INVALID_OP",
		"RDR_SES_REQ",
		"RDR_SES_ESTBL",
		"RDR_SES_END",
		"RDR_CONF_CHANGE_STATE",
		"RDR_CONF_PRIVATE_FUNC",
		"RDR_CONF_TEST",
		"RDR_CONF_LIST_EXT",
		"RDR_CONF_HELP",
		"RDR_CONF_AP_ID_CMP",
		"RDR_CONF_ABORT_CMD",
		"RDR_CONF_CONFIRM_CALLBACK",
		"RDR_CONF_MSG_CALLBACK",
		"RDR_RSRC_INFO"
	};

	assert(hdr);

	/* clamp an invalid opcode */
	if (hdr->message_opcode >= RDR_NUM_OPS) {
		hdr->message_opcode = 0;
	}

	/* clamp an invalid type */
	if (hdr->data_type > RDR_REPLY) {
		hdr->data_type = 0;
	}

	DCS_DBG(DBG_MSG, "message %s: <%s, %s%s>",
	    (type == DCS_RECEIVE) ? "received" : "sent",
	    op_str[hdr->message_opcode],
	    type_str[hdr->data_type],
	    ((hdr->data_type == RDR_REQUEST) ||
	    (hdr->message_opcode == RDR_CONF_AP_ID_CMP)) ? "" :
	    (hdr->status == RDR_SUCCESS) ? ", RDR_SUCCESS" :
	    ", RDR_FAILED");
}
